﻿namespace Triad.Controls
{
    using System;
    using System.ComponentModel;
    using System.Security.Permissions;
    using System.Text;
    using System.Web;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using System.Xml;
    using System.Data;
    using System.Web.UI.HtmlControls;
    using System.Configuration;

    [AspNetHostingPermission(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
    [ControlValueProperty("SelectedValue")]
    [DefaultEvent("IndexChanged")]
    [SupportsEventValidation]
    [AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
    [ToolboxData(@"<{0}:tfComboBox runat=server></{0}:tfComboBox>")]
    public partial class tfComboBox : ListControl, IPostBackEventHandler, IPostBackDataHandler, ICallbackEventHandler, ICustomTypeDescriptor
    { 

      
        //ICallbackContainer, ICallbackEventHandler, ICustomTypeDescriptor, INamingContainer, ICompositeControlDesignerAccessor  {
        private string _callbackEventArgument;
        private readonly WebControl m_Container;

        private static readonly object sender = new object();

        /// <summary>
        /// Initializes a new instance of the <see cref="tfComboBox"/> class.
        /// </summary>
        /// <remarks>
        /// Author: tim.fischer,   8/25/2008 - 1:49 PM
        /// Modified By: 
        /// </remarks>
        public tfComboBox()
        {
            FirstItemText = "Select A Value";
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="tfComboBox"/> class.
        /// </summary>
        /// <param name="container">The container.</param>
        /// <remarks>
        /// Author: tim.fischer,   8/25/2008 - 1:49 PM
        /// Modified By: 
        /// </remarks>
        public tfComboBox(WebControl container)
        {
            FirstItemText = "Select A Value";
            m_Container = container;
        }



        /// <summary>
        /// Gets the data source.
        /// </summary>
        /// <returns></returns>
        protected object MyGetDataSource()
        {
            object dataSource = null;
            if (DataSource != null)
            {
                dataSource = DataSource;
            }
            else if (!String.IsNullOrEmpty(DataSourceID))
            {
                object datasource = null;
                foreach (Control control in Parent.Controls)
                {
                    if (control.ID == DataSourceID)
                    {
                        datasource = control;
                    }
                }
                if (datasource != null)
                {
                    if (typeof (AccessDataSource) == datasource.GetType())
                    {
                        var accessDataSource = (AccessDataSource) datasource;
                        dataSource = accessDataSource.Select(new DataSourceSelectArguments());
                    }
                    else if (typeof (SqlDataSource) == datasource.GetType())
                    {
                        var sqlDataSource = (SqlDataSource) datasource;
                        dataSource = sqlDataSource.Select(new DataSourceSelectArguments());
                    }
                }
            }
            return dataSource;
        }
        private string FilltfCombo()
        {
            var ctrlXML = new StringBuilder();
            object dataSource = MyGetDataSource();
            if (dataSource != null)
            {
                if (!String.IsNullOrEmpty(DataValueField) && !String.IsNullOrEmpty(DataTextField))
                {
                    ctrlXML.Append("<ctrl id=\'" + ClientID + "\'>");
                    ctrlXML.AppendFormat("<opt value=\'\' selected=\'true\'>{0}</opt>", _firstItemText);
                    DataTable table;
                    if (dataSource is DataSet)
                        if (!string.IsNullOrEmpty(DataMember)) table = ((DataSet) dataSource).Tables[DataMember];
                        else table = ((DataSet) dataSource).Tables[0];
                    else if (dataSource is DataTable)
                    {
                        table = (DataTable)dataSource;
                    }
                    else 
                        table = ((DataView) dataSource).Table;

                    foreach (DataRow row in table.Rows)
                        ctrlXML.AppendFormat("<opt value=\'{0}\'>{1}</opt>", row[DataValueField], row[DataTextField]);

                   ctrlXML.Append("</ctrl>");
                }
            }else
                if (this.Items.Count > 0)
                {
                    ctrlXML.Append("<ctrl id=\'" + ClientID + "\'>");
                    if(!String.IsNullOrEmpty(FirstItemText))
                    {
                        ctrlXML.AppendFormat("<opt value=\'\' selected=\'true\'>{0}</opt>", FirstItemText);
                    }
                    foreach (ListItem li in this.Items)
                    {
                        ctrlXML.AppendFormat("<opt value=\'{0}\'>{1}</opt>", li.Value, li.Text);                        
                    }
                    ctrlXML.Append("</ctrl>");

                }
            return ctrlXML.ToString();
        }

        /// <summary>
        /// Raises the <see cref="E:System.Web.UI.Control.PreRender"/> event.
        /// </summary>
        /// <param name="e">An <see cref="T:System.EventArgs"/> that contains the event data.</param>
        /// <exclude/>

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers", MessageId = "0#")]
        protected override void OnPreRender(EventArgs e)
        {
            SetupClientScripts();
        }

        /// <summary>
        /// Renders the HTML opening tag of the control to the specified writer. This method is used primarily by control developers.
        /// </summary>
        /// <param name="writer">A <see cref="T:System.Web.UI.HtmlTextWriter"/> that represents the output stream to render HTML content on the client.</param>
        /// <remarks>
        /// Author: tim.fischer,   8/25/2008 - 1:41 PM
        /// Modified By: 
        /// </remarks>
        public override void RenderBeginTag(HtmlTextWriter writer)
        {
            writer.Write("<div id=\"{0}\">",ID);
        }

        /// <summary>
        /// Renders the HTML closing tag of the control into the specified writer. This method is used primarily by control developers.
        /// </summary>
        /// <param name="writer">A <see cref="T:System.Web.UI.HtmlTextWriter"/> that represents the output stream to render HTML content on the client.</param>
        /// <remarks>
        /// Author: tim.fischer,   8/25/2008 - 1:41 PM
        /// Modified By: 
        /// </remarks>
        public override void  RenderEndTag(HtmlTextWriter writer)
        {
            writer.WriteLine("</div><script type='text/javascript'>//<![CDATA[");
            writer.WriteLine(RegisterScript());
            writer.WriteLine("//]]></script>");
        }

        /// <summary>
        /// Outputs server control content to a provided <see cref="T:System.Web.UI.HtmlTextWriter"/> object and stores tracing information about the control if tracing is enabled.
        /// </summary>
        /// <param name="writer">The System.Web.UI.HTmlTextWriter object that receives the control content.</param>
        /// <remarks>
        /// Author: tim.fischer,   8/25/2008 - 1:41 PM
        /// Modified By:
        /// </remarks>
        public override void RenderControl(HtmlTextWriter writer)
        {
            //var shtml = new StringBuilder();

            if (IsDesignTime)
            {
                RenderDesignTimeCode(writer);
                return;
            }
            //RenderServerSideCode(output);
            if (AutoPostBack) { XMLtext.AutoPostBack = true; }

            EnsureChildControls();
            RenderBeginTag(writer);
            RenderChildren(writer);
            RenderEndTag(writer);
        }

        private void SetupClientScripts()
        {
            string sTmp = "~/_fw/controls/";
            if (!String.IsNullOrEmpty(ConfigurationManager.AppSettings["Triad.Controls"]))
            {
                sTmp = ConfigurationManager.AppSettings["Triad.Controls"];
            }
            string sTheme = "~/_fw/theme/red";
            if (!String.IsNullOrEmpty(ConfigurationManager.AppSettings["Theme"]))
            {
                sTheme = ConfigurationManager.AppSettings["Theme"];
            }

            ClientScriptManager cm = Page.ClientScript;
            if (!cm.IsClientScriptIncludeRegistered(typeof(Page), "tfcore"))
            {
                cm.RegisterClientScriptInclude("tfcore", ResolveClientUrl(sTmp + "js/tfcore.js"));
            }

            if (Page.Header.FindControl("tfcbCSS") == null)
            {
                if (!cm.IsClientScriptIncludeRegistered("tfComboBox"))
                {
                    cm.RegisterClientScriptInclude("tfComboBox",
                                                   ResolveClientUrl(sTmp+"js/tfcb.js"));
                }
                var cssLink = new HtmlLink
                {
                    Href = ResolveClientUrl(sTheme + "/tfcombobox.css"),
                    ID = "tfcbCSS"
                };
                cssLink.Attributes.Add("rel", "stylesheet");
                cssLink.Attributes.Add("type", "text/css");
                Page.Header.Controls.Add(cssLink);
            }

            var callbackScript = new StringBuilder();
            if (!cm.IsClientScriptBlockRegistered("InitCB_tfcb"))
            {

            callbackScript.Append("var AppComboImgPath='" + sTmp + "images/';" + Environment.NewLine);
            callbackScript.Append(
                "function CallbackRSD(result,context){if(context!=null&&result!=null){context.loadXMLString('<?xml version=\"1.0\" ?><list>'+result+");
            callbackScript.AppendLine("'</list>',context.getName());if(context.xmlList!=null){context.xmlList.value=result;}};return;}");

            cm.RegisterClientScriptBlock(GetType(), "InitCB_tfcb", callbackScript.ToString(), true);
            }

            callbackScript.Length = 0;
            if (!cm.IsClientScriptBlockRegistered(ClientID + "_tfcb"))
            {
                callbackScript.AppendFormat(" function {0}_change(){{ if({0}.getSelectedValue() != ''){{", ClientID);
                if (!String.IsNullOrEmpty(_linktfComboID))
                {
                    callbackScript.AppendFormat("   __theFormPostData = '';__theFormPostCollection = [];WebForm_InitCallback();WebForm_DoCallback('{1}','{1}|'+{1}.getSelectedIndex()+'|'+{1}.getSelectedValue()+'|{2}',CallbackRSD,{0},null,false);{0}.disable(false);{0}.redrawOptions();", _linktfComboID, ClientID, "true");
                }
                else
                {
                    callbackScript.AppendFormat("   __theFormPostData = '';__theFormPostCollection = [];WebForm_InitCallback();WebForm_DoCallback('{0}','{0}|'+{0}.getSelectedIndex()+'|'+{0}.getSelectedValue()+'|{2}',CallbackRSD,{0},null,false);{0}.redrawOptions();", ClientID, " ", "false");
                }
                callbackScript.AppendLine("}}");

                cm.RegisterClientScriptBlock(GetType(), ClientID + "_tfcb" + ClientID, callbackScript.ToString(), true);
            }
        }


        /// <summary>
        /// Gets the Selected items "Value" of the tfComboBox.
        /// </summary>
        /// <returns></returns>
        /// <remarks>
        /// Author: tim.fischer,   4/24/2008 - 2:43 PM
        /// Modified By:
        /// </remarks>
        public string GetSelectedValue()
        {
            //var xmlID = (TextBox) this.Page.FindControl(ClientID+"_XML");
            if (String.IsNullOrEmpty(this.XMLtext.Text)) return "0";
            var xmlDoc = new XmlDocument();
            xmlDoc.LoadXml(this.XMLtext.Text);
            return xmlDoc.SelectSingleNode("//opt[@selected=\'true\']") != null
                       ? xmlDoc.SelectSingleNode("//opt[@selected=\'true\']").Attributes["value"].InnerXml
                       : "0";
        }

        #region ICallbackEventHandler implementation

        /// <internalonly/>
        void ICallbackEventHandler.RaiseCallbackEvent(string eventArgument)
        {
            RaiseCallbackEvent(eventArgument);
        }

        string ICallbackEventHandler.GetCallbackResult()
        {
            return GetCallbackResult();
        }

        protected virtual void RaiseCallbackEvent(string eventArgument)
        {
            _callbackEventArgument = eventArgument;
        }

        protected virtual string GetCallbackResult()
        {
            // Do not take any callback into account if the tree is disabled.
            if (!IsEnabled) return String.Empty;
            //return this.XMLtext.Text;
            // Split the eventArgument into pieces
            // The format is (without the spaces):
            // linkedclientid | clientIndex | clientValue
            // nodeIndex | lastIndex | databound | parentIsLast | text.length | text datapath.Length | datapath path 

            //// The first piece is always the current ClientID
            int startIndex = 0;
            int endIndex = _callbackEventArgument.IndexOf('|');
            string _linkedIdString = _callbackEventArgument.Substring(startIndex, endIndex);

            //// The second piece is either the current ClientID or the LinkedID
            startIndex = endIndex + 1;
            endIndex = _callbackEventArgument.IndexOf('|', startIndex);
            string _clientIdIndex = _callbackEventArgument.Substring(startIndex, endIndex - startIndex);

            //// The third piece is either the current ClientID or the LinkedID
            startIndex = endIndex + 1;
            endIndex = _callbackEventArgument.IndexOf('|', startIndex);
            string _clientIdValue = _callbackEventArgument.Substring(startIndex, endIndex - startIndex);

            //Last piece is boolean whether or not it is linked.
            startIndex = _callbackEventArgument.LastIndexOf('|');
            string _isLinked = _callbackEventArgument.Substring(startIndex+1).ToLower();

            string result = String.Empty;
            result = this.XMLtext.Text;

            if (!String.IsNullOrEmpty(_clientIdValue) && _isLinked == "true")
            {
                result = OnComboBoxIndexChanged(new ComboBoxItemChangeEventArgs(Int32.Parse(_clientIdValue), result));
            }
            
            _callbackEventArgument = String.Empty;
            return result;
        }
        #endregion
        
        /// <summary>
        /// Encode text to be passed as a URL or within javascript
        /// </summary>
        /// <param name="value"></param>
        /// <param name="forUrl"></param>
        /// <returns></returns>
        static string JScriptEncode(string value, Boolean forUrl)
        {
            if (String.IsNullOrEmpty(value)) return value;
            string sbuilder = value.Replace("%", "%%");
            sbuilder = sbuilder.Replace("\\", "\\\\");
            sbuilder = sbuilder.Replace("'", "\\'");
            sbuilder = sbuilder.Replace(Convert.ToString((char) 9), "\\t");
            sbuilder = sbuilder.Replace(Convert.ToString((char) 10), "\\n");
            sbuilder = sbuilder.Replace(Convert.ToString((char) 13), "\\r");
            sbuilder = sbuilder.Replace(Convert.ToString((char) 34), "\\" + Convert.ToString((char) 34));
            sbuilder = sbuilder.Replace("<", "&lt");
            sbuilder = sbuilder.Replace(">", "&gt");
            //\' - single quote, needed for character literals 
            //\" - double quote, needed for string literals 
            //\\ - backslash 
            //\0 - Unicode character 0 
            //\a - Alert (character 7) 
            //\b - Backspace (character 8) 
            //\f - Form feed (character 12) 
            //\n - New line (character 10) 
            //\r - Carriage return (character 13) 
            //\t - Horizontal tab (character 9) 
            //\v - Vertical quote (character 11) 
            if (forUrl) sbuilder = sbuilder.Replace(" ", "%20");
            return sbuilder;
        }

        /// <summary>
        /// Creates a script for initiating a client callback to a Web server.
        /// </summary>
        /// <param name="buttonControl">The control initiating the callback request.</param>
        /// <param name="argument">The arguments used to build the callback script.</param>
        /// <returns>
        /// A script that, when run on a client, will initiate a callback to the Web server.
        /// </returns>
        /// <remarks>
        /// Author: tim.fischer,   8/25/2008 - 1:41 PM
        /// Modified By: 
        /// </remarks>
        public string GetCallbackScript(IButtonControl buttonControl, string argument)
        {
            throw new NotImplementedException();
        }


        /// <summary>
        /// In a control derived from <see cref="T:System.Web.UI.WebControls.CompositeControl"/>, recreates the child controls at design time. Called by the control's associated designer.
        /// </summary>
        public virtual void RecreateChildControls()
        {
            ChildControlsCreated = false;
            EnsureChildControls();
        }


        #region IPostBackEventHandler Members

        public void RaisePostBackEvent(string eventArgument)
        {
            throw new NotImplementedException();
        }

        #endregion

        //public delegate string ComboBoxEventHandler(object Sender, ComboBoxItemChangeEventArgs e);

        internal virtual string OnComboBoxIndexChanged(ComboBoxItemChangeEventArgs e)
        {
            //ComboBoxEventHandler handler = (ComboBoxEventHandler)Events[sender];
            //if (handler != null)
            //{
            //    return handler(this, e);
            //}
            return null;
        }

        [WebCategory("Behavior")]
        public event EventHandler<ComboBoxItemChangeEventArgs> ComboBoxIndexChanged
        {
            add
            {
                Events.AddHandler(sender, value);
            }
            remove
            {
                Events.RemoveHandler(sender, value);
            }
        }



        #region IPostBackDataHandler Members

        public bool LoadPostData(string postDataKey, System.Collections.Specialized.NameValueCollection postCollection)
        {
            return true;
            //throw new NotImplementedException();
        }

        public void RaisePostDataChangedEvent()
        {
            //throw new NotImplementedException();
        }

        #endregion
    } // end class

    #region "WebCategoryAttribute"

    [AttributeUsage(AttributeTargets.All)]
    internal sealed class WebCategoryAttribute : CategoryAttribute
    {
        internal WebCategoryAttribute(string category)
            : base(category)
        {
        }

        protected override string GetLocalizedString(string value)
        {
            string localizedString = base.GetLocalizedString(value);
            return localizedString;
        }

        public override object TypeId
        {
            get
            {
                return typeof(CategoryAttribute);
            }
        }
    }

    #endregion

#region "WebSysDefaultValueAttribute"

    [AttributeUsage(AttributeTargets.All)]
    internal sealed class WebSysDefaultValueAttribute : DefaultValueAttribute
    {
        // Fields
        private bool _localized;
        private Type _type;

        // Methods
        internal WebSysDefaultValueAttribute(string value)
            : base(value)
        {
        }

        internal WebSysDefaultValueAttribute(Type type, string value)
            : base(value)
        {
            this._type = type;
        }

        // Properties
        public override object TypeId
        {
            get
            {
                return typeof(DefaultValueAttribute);
            }
        }

        public override object Value
        {
            get
            {
                if (!this._localized)
                {
                    this._localized = true;
                    string str = (string)base.Value;
                    if (!string.IsNullOrEmpty(str))
                    {
                        object obj2 = str;
                        if (this._type != null)
                        {
                            try
                            {
                                obj2 = TypeDescriptor.GetConverter(this._type).ConvertFromInvariantString((string)obj2);
                            }
                            catch (NotSupportedException)
                            {
                                obj2 = null;
                            }
                        }
                        base.SetValue(obj2);
                    }
                }
                return base.Value;
            }
        }
    }

    #endregion

    #region "WebSysDescriptionAttribute"

    [AttributeUsage(AttributeTargets.All)]
    internal class WebSysDescriptionAttribute : DescriptionAttribute
    {
        private bool replaced;

        internal WebSysDescriptionAttribute(string description)
            : base(description)
        {
        }

        public override string Description
        {
            get
            {
                if (!this.replaced)
                {
                    this.replaced = true;
                    base.DescriptionValue = base.Description;
                }
                return base.Description;
            }
        }

        public override object TypeId
        {
            get
            {
                return typeof(DescriptionAttribute);
            }
        }
    }

    #endregion

    #region "WebSysDisplayNameAttribute"

    [AttributeUsage(AttributeTargets.Event | AttributeTargets.Property | AttributeTargets.Class)]
    internal sealed class WebSysDisplayNameAttribute : DisplayNameAttribute
    {
        // Fields
        private bool replaced;

        // Methods
        internal WebSysDisplayNameAttribute(string DisplayName)
            : base(DisplayName)
        {
        }

        // Properties
        public override string DisplayName
        {
            get
            {
                if (!this.replaced)
                {
                    this.replaced = true;
                    base.DisplayNameValue = base.DisplayName;
                }
                return base.DisplayName;
            }
        }

        public override object TypeId
        {
            get
            {
                return typeof(DisplayNameAttribute);
            }
        }
    }

    #endregion
} // end namespace